from __future__ import annotations

from io import BytesIO
from pyvision.helpler.Importer import import_dependency
from abc import abstractmethod
from pyvision.core.Props import AltChartProps, DataSetProps, RawHtmlProps, TableColumnProps, TableProps

from pyvision.core.Enums import ComponentModelTagEnum
from typing import TYPE_CHECKING, Dict

from pyvision.core.DataModel import ComponentModel
from pyvision.core.Props import SelectProps

import jinja2 as jj


if TYPE_CHECKING:
    from pyvision.wrappers.DataSet import DataSet


class Component():
    def __init__(self, id: str) -> None:
        self.cp = ComponentModel(id)

    @abstractmethod
    def _get_component_model(self) -> ComponentModel:
        pass


class DataSetComponent(Component):

    def __init__(self, id: str, dataSet: DataSet) -> None:
        super().__init__(id)
        self.dataSet = dataSet


class SlicerComponent(DataSetComponent):

    def __init__(self, id: str, dataSet: DataSet, col_name: str) -> None:
        super().__init__(id, dataSet)
        assert col_name, 'column name must be set'
        self.col_name = col_name
        self.attach_dataSet_mes = {}

    def attach_dataset(self, ds: DataSet, column_name: str = ''):
        self.attach_dataSet_mes.update({ds.id: column_name})
        return self


class RawHtml(Component):

    def __init__(self, id: str, html: str) -> None:
        super().__init__(id)
        self.html = html

    def _get_component_model(self) -> ComponentModel:
        p = RawHtmlProps(self.html)
        res = self.cp.set_tag(ComponentModelTagEnum.RawHtml).set_props(p)
        return res


class MplChart(RawHtml):
    # mpl = import_dependency('matplotlib')

    def __init__(self, id: str, fig) -> None:
        self.fig = fig
        html = self._create_svg()
        super().__init__(id, html)

    def _create_svg(self):
        f = BytesIO()
        self.fig.savefig(f, format='svg')
        # bs = BeautifulSoup(f.getvalue(), "xml")
        # tree, xmlid = ET.XMLID(f.getvalue())
        # tree.set('height', '345.6px')
        # tree.set('width', '460.8px')
        # tree.set('preserveAspectRatio', 'xMinYMin meet')

        # # MatplotlibFigureContent._set_obj_metadata(fig,xmlid)
        # res = ET.tostring(tree, encoding='unicode')

        return f.getvalue().decode(encoding='utf8')


class AltairChart(RawHtml):
    alt = import_dependency('altair')

    def __init__(self, id: str, chart: AltairChart.alt.Chart) -> None:
        self.chart = chart
        html = self._create_html()
        super().__init__(id, html)

    def _create_html(self):
        pass


class Table(DataSetComponent):

    def __init__(self, id: str, dataSet: DataSet) -> None:
        super().__init__(id, dataSet)
        self.col_name_mapping = {}

    def _get_component_model(self) -> ComponentModel:
        p = TableProps(self.dataSet)

        for k, v in self.col_name_mapping.items():
            p.set_columns(TableColumnProps(k, v))

        res = self.cp.set_tag(ComponentModelTagEnum.Table).set_props(p)
        return res

    def set_column_name(self, **name_mapping: str):
        self.col_name_mapping.update(name_mapping)
        return self


class AltChart(DataSetComponent):

    def __init__(self, id: str, dataSet: DataSet, spec: dict = None) -> None:
        super().__init__(id, dataSet)
        spec = spec or {}
        self.spec = spec

    def _get_component_model(self) -> ComponentModel:
        p = AltChartProps(self.dataSet)
        p.spec = self.spec
        res = self.cp.set_tag(ComponentModelTagEnum.AltChart).set_props(p)
        return res


class Select(SlicerComponent):

    def __init__(self, id: str, dataSet: DataSet, col_name: str) -> None:
        super().__init__(id, dataSet, col_name)

    def _get_component_model(self) -> ComponentModel:
        p = SelectProps().set_source(self.dataSet.id)
        p.attach_dataset(DataSetProps(self.dataSet.id, self.col_name))

        for k, v in self.attach_dataSet_mes.items():
            v = v or self.col_name
            p.attach_dataset(DataSetProps(k, v))

        res = self.cp.set_tag(ComponentModelTagEnum.Select).set_props(p)
        return res


class DatePicker(SlicerComponent):

    def __init__(self, id: str, dataSet: DataSet, col_name: str) -> None:
        super().__init__(id, dataSet, col_name)

    def _get_component_model(self) -> ComponentModel:
        p = SelectProps().set_source(self.dataSet.id)
        p.attach_dataset(DataSetProps(self.dataSet.id, self.col_name))

        for k, v in self.attach_dataSet_mes.items():
            v = v or self.col_name
            p.attach_dataset(DataSetProps(k, v))

        res = self.cp.set_tag(ComponentModelTagEnum.DatePicker).set_props(p)
        return res
